package com.syher.others.design.proxy.dynamic;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理
 * 优点：  代码无侵入式扩展、
 * 缺点： jdk的代理机制必须要求被代理类实现某个方法。
 *        一个没有实现任何接口的类是无法通过jdk的代理机制进行代理
 *        如果实现类存在自定义的方法，该方法也无法被jdk代理
 *        解决方法是使用cglib进行代理
 * @author shenyuhang
 * @date 2018/12/24.
 **/
public class JDKProxyHandler<T> implements InvocationHandler {

    // invocationHandler持有的被代理对象
    T target;

    public JDKProxyHandler(){}

    public JDKProxyHandler(T target) {
        this.target = target;
    }

    public T bind(T target) {
        this.target = target;
        return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * proxy:代表动态代理对象
     * method：代表正在执行的方法
     * args：代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before doSomething...");
        return method.invoke(target, args);
    }

    interface Subject {
        /**
         * 接口方法
         */
        void doSomething();
    }

    static class RealSubject implements Subject {

        @Override
        public void doSomething() {
            System.out.println("doSomething() method has been called.");
        }
    }

    public static void main(String[] args) {
        // 1 创建一个与代理对象相关联的InvocationHandler
        InvocationHandler handler = new JDKProxyHandler<Subject>(new RealSubject());

        /**
         * 第一种
         */
        // 2 生成一个动态代理类proxyClass
        Class<?> proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), new Class<?>[]{Subject.class});

        try {
            // 3 获取带InvocationHandler参数的构造器constructor
            Constructor<?> constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
            // 4 创建一个动态实例subject
            Subject subject = (Subject)constructor.newInstance(handler);
            subject.doSomething();
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**
         * 第二种 就是对第一种方式(2 3 4步骤)的封装
         */
        Subject subject = new JDKProxyHandler<Subject>().bind(new RealSubject());

        subject.doSomething();
    }
}
